#include <winsock2.h>
#include <stdio.h>
#include <windows.h>


#include "include/server.h"

#pragma comment(lib, "ws2_32.lib")

/*

Copyright (c)  2001-2007 xiaozhi

Module name :

	server.c
	
Abstract :	
	
	TCP/IP  server with implement .

Author :
	
	xiaozhi

Date:

	2007/1/4   21:36

Modify :

Description:
    This example illustrates a simple TCP server that accepts 
    incoming client connections. Once a client connection is 
    established, a thread is spawned to read data from the 
    client and echo it back (if the echo option is not 
    disabled).

Compile:
    cl -o Server Server.c ws2_32.lib

Command line options:
    server [-p:x] [-i:IP] [-o]
           -p:x      Port number to listen on
           -i:str    Interface to listen on
           -o        Receive only, don't echo the data back


*/


int    iPort      = DEFAULT_PORT;	/* Port to listen for clients on      */
BOOL   bInterface = FALSE,		/* Listen on the specified interface  */
       bRecvOnly  = FALSE;		/* Receive data only; don't echo back */
char   szAddress[128];			/* Interface to listen for clients on */

/*
 * Description:
 *    Print usage information and exit
 */
void usage()
{
	printf("usage: server [-p:x] [-i:IP] [-o]\n\n");
	printf("       -p:x      Port number to listen on\n");
	printf("       -i:str    Interface to listen on\n");
	printf("       -o        Don't echo the data back\n\n");
	ExitProcess(1);
}

/*
 * Description:
 *    Parse the command line arguments, and set some global flags 
 *    to indicate what actions to perform
 */
void ValidateArgs(int argc, char **argv)
{
	int i;
	
	for(i = 1; i < argc; i++)
	{
		if ((argv[i][0] == '-') || (argv[i][0] == '/'))
		{
			switch (tolower(argv[i][1]))
			{
			case 'p':
				iPort = atoi(&argv[i][3]);
				break;
			case 'i':
				bInterface = TRUE;
				if (strlen(argv[i]) > 3)
					strcpy(szAddress, &argv[i][3]);
				break;
			case 'o':
				bRecvOnly = TRUE;
				break;
			default:
				usage();
				break;
			}
		}
	}
}

/*
 * Description:
 *    This function is called as a thread, and it handles a given
 *    client connection.  The parameter passed in is the socket 
 *    handle returned from an accept() call.  This function reads
 *    data from the client and writes it back.
 */
DWORD WINAPI ClientThread(LPVOID lpParam)
{
	SOCKET        sock = (SOCKET)lpParam;

	char          szBuff[DEFAULT_BUFFER];
	int           ret, nLeft, idx;
	
	while(1)
	{
		/* Perform a blocking recv() call */ 
		ret = recv(sock, szBuff, DEFAULT_BUFFER, 0);
		if (ret == 0)        // Graceful close
			break;
		else if (ret == SOCKET_ERROR)
		{
			printf("recv() failed: %d\n", WSAGetLastError());
			break;
		}
		szBuff[ret] = '\0';
		printf("RECV: '%s'\n", szBuff);

		/* If we selected to echo the data back, do it  */
		if (!bRecvOnly)
		{
			nLeft = ret;
			idx = 0;
		
			/* Make sure we write all the data */
			while(nLeft > 0)
			{
				ret = send(sock, &szBuff[idx], nLeft, 0);
				if (ret == 0)
					break;
				else if (ret == SOCKET_ERROR)
				{
					printf("send() failed: %d\n", 
						WSAGetLastError());
					break;
				}
				nLeft -= ret;
				idx += ret;
			}
		}
	}
	return 0;
}

/*
 * Description:
 *    Main thread of execution. Initialize Winsock, parse the
 *    command line arguments, create the listening socket, bind
 *    to the local address, and wait for client connections.
 */
int main(int argc, char **argv)
{
	WSADATA       wsd;
	SOCKET        server, sClient;
	int           iAddrSize;
	HANDLE        hThread;
	DWORD         dwThreadId;
	struct sockaddr_in local,
			client;
	
	ValidateArgs(argc, argv); 

	if (WSAStartup(MAKEWORD(2,2), &wsd) != 0)
	{
		printf("Failed to load Winsock!\n");
		return 1;
	} 

	/* Create our listening socket */
	server = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
	if (server == SOCKET_ERROR)
	{
		printf("socket() failed: %d\n", WSAGetLastError());
		return 1;
	}

	/*  Select the local interface and bind to it */
	if (bInterface)
	{
		local.sin_addr.s_addr = inet_addr(szAddress);
		if (local.sin_addr.s_addr == INADDR_NONE)
			usage();
	}
	else
		local.sin_addr.s_addr = htonl(INADDR_ANY);
		local.sin_family = AF_INET;
		local.sin_port = htons(iPort);
	
	if (bind(server, (struct sockaddr *)&local, 
		sizeof(local)) == SOCKET_ERROR)
	{
		printf("bind() failed: %d\n", WSAGetLastError());
		return 1;
	}
	listen(server, 8);

	/*
	 * In a continous loop, wait for incoming clients. Once one 
	 * is detected, create a thread and pass the handle off to it.
	 */
	while (1)
	{
		iAddrSize = sizeof(client);
		sClient = accept(server, (struct sockaddr *)&client,
                        &iAddrSize);        
		if (sClient == INVALID_SOCKET)
		{        
			printf("accept() failed: %d\n", WSAGetLastError());
			break;
		}
		printf("Accepted client: %s:%d\n", 
			inet_ntoa(client.sin_addr), ntohs(client.sin_port));
		
		hThread = CreateThread(NULL, 0, ClientThread, 
			(LPVOID)sClient, 0, &dwThreadId);
		if (hThread == NULL)
		{
			printf("CreateThread() failed: %d\n", GetLastError());
			break;
		}
		CloseHandle(hThread);
	}
	closesocket(server);
	
	WSACleanup();
	return 0;
}
